use rand::TryRngCore;

use anyhow::{Context, Result, anyhow};

#[inline]
pub fn gen_id() -> Result<i64> {
    Ok((rand::rngs::OsRng.try_next_u64().with_context(|| "Failed to generate ID")? & 0x7FFFFFFFFFFFFFFF) as i64)
}

#[inline]
pub fn id2str(id: i64) -> String {
    format!("{:04x}-{:04x}-{:08x}", (id >> 48) & 0xFFFF, (id >> 32) & 0xFFFF, id & 0xFFFFFFFF)
}

#[inline]
pub fn str2id(s: &str) -> Result<i64> {
    let parts: Vec<&str> = s.split('-').collect();
    if parts.len() != 3 {
        return Err(anyhow!("Invalid ID format"));
    }
    Ok((i64::from_str_radix(parts[0], 16)? << 48) | (i64::from_str_radix(parts[1], 16)? << 32) | i64::from_str_radix(parts[2], 16)?)
}

#[inline]
pub fn opt_str2id(str: &Option<String>) -> Result<Option<i64>> {
    match str {
        Some(str) => Ok(Some(str2id(&str)?)),
        None => Ok(None),
    }
}

#[inline]
pub fn opt_id2str(id: &Option<i64>) -> Option<String> {
    match id {
        Some(id) => Some(id2str(*id)),
        None => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        for _ in 0..1000 {
            let id: i64 = gen_id().unwrap();
            let s = id2str(id);
            let id2 = str2id(&s).unwrap();
            assert_eq!(id, id2);
        }
    }

    #[test]
    fn test2() {
        let id: i64 = 0;
        let str = format!("{:04x}-{:04x}-{:08x}", (id >> 48) & 0xFFFF, (id >> 32) & 0xFFFF, id & 0xFFFFFFFF);

        println!("{}", str);
    }
}
